/**
 * MP Software.
 */
package service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import models.Deduction;
import models.Payment;
import models.Payroll;
import models.PayrollPeriod;
import models.Rate;
import play.modules.guice.InjectSupport;

/**
 * @author Jeremiah John M. Macariola
 * 
 */
@InjectSupport
public class PayrollRegisterServiceImpl implements PayrollRegisterService {

	@Inject
	private RateResolver rateResolver;

	@Inject
	private PayrollCalculatorFacade payrollCalculatorFacade;

	public PayrollRegisterResult process(String electronicId,
			PayrollPeriod payrollPeriod) {
		List<Payroll> payrolls = loadPayrolls(payrollPeriod);

		Map<Long, Payroll> employeePayrolls = new HashMap<Long, Payroll>();
		Map<String, Float> employeePayments = new HashMap<String, Float>();
		Map<String, BigDecimal> employeeDeductions = new HashMap<String, BigDecimal>();
		Map<Long, CalculationResult> calculationResults = new HashMap<Long, CalculationResult>();
		for (Payroll payroll : payrolls) {
			employeePayrolls.put(payroll.employee.id, payroll);
			List<Payment> payments = Payment.find("payroll", payroll).fetch();
			for (Payment payment : payments) {
				employeePayments.put(payroll.employee.id + payment.paymentType,
						payment.hours);
			}

			List<Deduction> deductions = Deduction.find("payroll", payroll)
					.fetch();
			for (Deduction deduction : deductions) {
				employeeDeductions.put(payroll.employee.id
						+ deduction.deductionType, deduction.value);
			}

			/*
			 * Get the payroll calculation result.
			 */
			Rate rate = rateResolver.getRate(electronicId);
			CalculationResult calculationResult = payrollCalculatorFacade
					.calculate(electronicId, rate, payments, deductions);

			calculationResults.put(payroll.employee.id, calculationResult);
		}

		PayrollRegisterResult result = new PayrollRegisterResult();
		result.setEmployeePayrolls(employeePayrolls);
		result.setEmployeePayments(employeePayments);
		result.setEmployeeDeductions(employeeDeductions);
		result.setCalculationResults(calculationResults);

		return result;
	}

	/**
	 * <p>
	 * Loads all the payrolls for the specified <code>payrollPeriod</code> and
	 * removes any duplicate in the result. This implementation relies on
	 * <code>hashCode()</code> and
	 * <code>equals()<code> methods to determine the uniqueness of the element.
	 * </p>
	 */
	public List<Payroll> loadPayrolls(PayrollPeriod payrollPeriod) {
		List<Payroll> payrolls = Payroll.find(
				"payrollPeriod = ? order by employee.name asc", payrollPeriod)
				.fetch();

		Set<Payroll> uniquePayrolls = new LinkedHashSet<Payroll>(payrolls);

		return new ArrayList<Payroll>(uniquePayrolls);
	}

}
